<?php

/*
	Class: Commerce_Magento_Catalog

	About: Author
		Rich Joslin, Tyler Vigeant, Swift Collective

	About: License
		<http://communit.as/docs/license>
*/
class CommerceMagentoCatalog extends Commerce_Db_Model_Magento {

	/*
		Variable: $pagination
	*/
	public $pagination	= array();

	/*
		Variable: $logging
	*/
	public $logging = true;

	/*
		Function: _filter_pagination
	*/
	protected function _filter_pagination( $arg, $key='products') {
		if (is_array($arg) && !empty($arg) && array_key_exists('__pagination_per', $arg)) {
			$this->pagination[$key]['per'] = $arg['__pagination_per'];
			$this->pagination[$key]['current'] = $arg['__pagination_current'];
			$this->pagination[$key]['total'] = $arg['__pagination_total'];
			$this->pagination[$key]['total_pages'] = ceil($arg['__pagination_total']/$arg['__pagination_per']);
			unset($arg['__pagination_per']);
			unset($arg['__pagination_current']);
			unset($arg['__pagination_total']);
		}
		return $arg;
	}
	
	/*
		Function: formatStoreId

		Arguments:
			$store_id - The ID or code of the store in the Magento database.
	*/
	function formatStoreId($storeView) {
		return (is_string($storeView)) ? strtolower(str_replace(array('-', ' '), '_', $storeView)) : (int)$storeView; // Convert to magento store code standards (a-z) (0-9) (_)
	}
	
	/* Group: Instance Methods */

	/*
		Function: getStore
	*/
	function getStore() {
		$this->initSoap();
		return $this->client->call(
			$this->_api_session_id,
			'catalog_product.currentStore',
			array()
		);
	}

	/*
		Function: setStore

		Arguments:
			$storeView - The ID or code of the store in the Magento database.
	*/
	function setStore($storeView) {
		$storeView = $this->formatStoreId($storeView);
		$this->initSoap();
		return $this->client->call(
			$this->_api_session_id,
			'catalog_product.currentStore',
			array('storeView' => $storeView)
		);
	}
	
	
	function getStores() {
		$this->initSoap();
		$output = $this->client->call(
			$this->_api_session_id,
			'catalog_store.getStores'
		);
		return $output;
	}
	
	function getCurrentSeason($storeId, $storeView = false) {
		$season_id = $this->getCurrentSeasonId($storeId);
		return $this->getCategory($season_id, $storeView);
	}
	
	function getCurrentSeasonId($storeId) {
		$storeId = $this->formatStoreId($storeId);
		
		$cache_name = __function__;
		$cache_name .= '_'.$storeId;
		$cache_tags = array('get_season_id', (string)$storeId);
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			try {
				$this->initSoap();
				$output = $this->client->call(
					$this->_api_session_id,
					'catalog_store.getRootCategoryId',
					array($storeId)
				);
			} catch (Exception $e) {
				$output = null;
			}
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}
	
	function setCurrentSeason( $storeId, $categoryId ) {
		$storeId = $this->formatStoreId($storeId);
		
		$this->initSoap();
		$output = $this->client->call(
			$this->_api_session_id,
			'catalog_store.setRootCategoryId',
			array($storeId, $categoryId)
		);
		
		if($this->logging) Cts_Log::report("Cache Clean :: Run by setCurrentSeason()", NULL, Zend_Log::INFO);
		if ($this->logging) Cts_Log::report("Cache Clean :: get_season_id", NULL, Zend_Log::INFO);
		$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_season_id', (string)$storeId));
		if ($this->logging) Cts_Log::report("Cache Clean :: category_tree", NULL, Zend_Log::INFO);
		$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_tree'));
		
		return $output;
	}
	
	function getStock($productId) {
		$this->initSoap();
		$output = $this->client->call(
			$this->_api_session_id,
			'cataloginventory_stock_item.list',
			array('productId'=>$productId)
		);
		return $output;
	}
	
	/*
		Function: setStockDefault
		Normally we would not cache a method that sets a value, but this method is 
		called on all PLPs to correct a wide spread data issue within Magento.
	*/
	function setStockDefault($productId) {
		$cache_name = __function__;
		$cache_name .= '_'.$productId;
		$cache_tags = array('product_stock_default', (string)$productId);
		$output = $this->cache->load($cache_name);
		
		if ($output === false || !isset($output)) {
			try {
				$this->initSoap();
				$output = $this->client->call(
					$this->_api_session_id,
					'cataloginventory_stock_item.update',
					array(
						'productId'=>$productId,
						array('use_config_manage_stock'=>true)
					)
				);
			} catch (Exception $e) {
				$output = null;
			}
			$this->cache->save($output, $cache_name, $cache_tags);
		} else {
			$output = 'cached';
		}
		return $output;
	}

	/*
		Function: getCategory

		Arguments:
			$category_id - The ID of the category in the Magento database.
	*/
	function getCategory($category_id, $storeView = false) {
		$cache_name = __function__;
		$cache_name .= '_'.$category_id;
		$cache_tags = array('get_category', (string)$category_id);
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= "_".$storeView;
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			try {
				$this->initSoap();
				$output = $this->client->call(
					$this->_api_session_id,
					'category.info',
					array($category_id, $storeView)
				);
			} catch (Exception $e) {
				$output = null;
			}
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getCategoryByUrlKey
	*/
	function getCategoryIdByUrlKey($url_key, $storeView = false, $multiple = false) {
		$cache_name = __function__;
		$cache_name .= '_'.str_replace('-', '_', $url_key);
		$cache_tags = array('url_key', str_replace('-', '_', $url_key), 'category_url_key');
		$args = array();
		$args['url_key'] = $url_key;
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= "_".$storeView;
		}
		if ($multiple) {
			$cache_name .= '_multiple';
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_category.getIdByUrlKey',
				$args
			);
			if( $multiple==false && is_array($output) && !empty($output) ){
				$output = $output[0];
			}
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getCategoryFilters
	*/
	function getCategoryFilters($category_id) {
		$cache_name = __function__;
		$cache_name .= '_'.$category_id;
		$cache_tags = array('category_filters', (string)$category_id);
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_category.getFilters',
				array($category_id)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getCategoryTree

		Arguments:
			$category_id - The ID of the category in the Magento database.
	*/
	function getCategoryTree($parentCategory = false, $storeView = false) {
		$cache_name = __function__;
		$cache_tags = array('category_tree');
		$args = array();
		if ($parentCategory) {
			$args['parentCategory'] = $parentCategory;
			$cache_name .= '_'.$parentCategory;
		}
		if ($storeView) {
			$args['storeView'] = $this->formatStoreId($storeView);
			$cache_name .= '_'.$args['storeView'];
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'category.tree',
				$args
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output['children'];
	}

	// /*
	// 	Function: getKits
	// */
	// function getKits($storeView = false) {
	// 	$cache_name = __function__;
	// 	$cache_tags = array('get_kits');
	// 	$args = array();
	// 	if ($storeView) {
	// 		$args['storeView'] = $this->formatStoreId($storeView);
	// 		$cache_name .= '_'.$args['storeView'];
	// 	}
	// 	// $output = $this->cache->load($cache_name);
	// 	// if ($output === false || !isset($output)) {
	// 		$this->initSoap();
	// 		$output = $this->client->call(
	// 			$this->_api_session_id,
	// 			'category.getKits',
	// 			$args
	// 		);
	// 	// 	$this->cache->save($output, $cache_name, $cache_tags);
	// 	// }
	// 	return $output;
	// }

	/*
		Function: updateCategory

		Arguments:
			$category_id - The ID of the category in the Magento database.
			$args - An array of database attribute=>value pairs
	*/
	function updateCategory($category_id, $args, $storeView = false) {
		$this->initSoap();
		$update = $this->client->call(
			$this->_api_session_id,
			'category.update',
			array($category_id, $args, $storeView)
		);
		if ($update) {
			if($this->logging) Cts_Log::report("Cache Clean :: Run by updateCategory()", NULL, Zend_Log::INFO);
			if ($this->logging) Cts_Log::report("Cache Clean :: category_tree", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_tree'));
			if ($this->logging) Cts_Log::report("Cache Clean :: get_category, {$category_id}", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_category', (string)$category_id));
		}
		return $update;
	}
	
	
	/*
		Function: createCategory

		Arguments:
			$parent_id - The ID of the parent category in the Magento database.
			$args - An array of database attribute=>value pairs
	*/
	function createCategory($parent_id, $args) {
		$this->initSoap();
		$create = $this->client->call(
			$this->_api_session_id,
			'category.create',
			array($parent_id, $args)
		);
		if ($create) {
			if($this->logging) Cts_Log::report("Cache Clean :: Run by createCategory()", NULL, Zend_Log::INFO);
			if ($this->logging) Cts_Log::report("Cache Clean :: category_tree", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_tree'));
		}
		return $create;
	}
	
	
	public function associateProducts( $category_id, $product_ids, $storeView=null ){
		$this->initSoap();
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
		}
		$update = $this->client->call(
			$this->_api_session_id,
			'category.associateProducts',
			array($category_id, $product_ids, $storeView)
		);
		if( $update && is_array($update) ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by associateProducts()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: get_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: category_filters, {$category_id}", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_filters', (string)$category_id));
			if($this->logging) Cts_Log::report("Cache Clean :: get_specials", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_specials'));
					
			if( is_array($update) ){
				$exec_time_add = 3*count($update);
				$exec_time = ini_get('max_execution_time')+$exec_time_add;
				if($this->logging) Cts_Log::report("Cache Clean :: Extending execution time by {$exec_time_add} seconds to {$exec_time} seconds", NULL, Zend_Log::INFO);
				set_time_limit($exec_time);
				foreach( $update as $product_id ){
					if($this->logging) Cts_Log::report("Cache Clean :: product, {$product_id}", NULL, Zend_Log::INFO);
					$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product', (string)$product_id));
				}
			}
		}
		return $update;
	}
	
	
	public function disassociateProducts( $category_id, $product_ids, $storeView=null ){
		$this->initSoap();
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
		}
		$update = $this->client->call(
			$this->_api_session_id,
			'category.disassociateProducts',
			array($category_id, $product_ids, $storeView)
		);
		if( $update && is_array($update) ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by disassociateProducts()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: get_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: category_filters, {$category_id}", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_filters', (string)$category_id));
			if($this->logging) Cts_Log::report("Cache Clean :: get_specials", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_specials'));
					
			if( is_array($update) ){
				$exec_time_add = 3*count($update);
				$exec_time = ini_get('max_execution_time')+$exec_time_add;
				if($this->logging) Cts_Log::report("Cache Clean :: Extending execution time by {$exec_time_add} seconds to {$exec_time} seconds", NULL, Zend_Log::INFO);
				set_time_limit($exec_time);
				foreach( $update as $product_id ){
					if($this->logging) Cts_Log::report("Cache Clean :: product, {$product_id}", NULL, Zend_Log::INFO);
					$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product', (string)$product_id));
				}
			}
		}
		return $update;
	}
	

	/*
		Function: moveCategory

		Arguments:
			$category_id - The ID of the category in the Magento database.
			$args - An array of database attribute=>value pairs
	*/
	function moveCategory($categoryId, $parentId, $afterId = false) {
		$this->initSoap();
		$move = $this->client->call(
			$this->_api_session_id,
			'category.move',
			array('categoryId' => $categoryId, 'parentId' => $parentId, 'afterId' => $afterId)
		);
		
		if( $move ){
			if($this->logging) Cts_Log::report("Cache Clean :: category_tree", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_tree'));
		}
		
		return $move;
	}

	/*
		Function: getProducts

		Arguments:
			$filters - An array of of arrays. Example: $filters = array( 'sku'=>array('like'=>'abc%') ); Default is an empty array.
			$storeView - An integer of the Store ID, or a string of the Store Code. Default is null.
			$attributes - An array of the names of attributes to get in addition to the basic ones.
	*/
	function getProducts($filters = array(), $storeView = null, $attributes = null, $sort = null) {
		$cache_name = __function__;
		$cache_name .= '_'.md5(serialize($filters));
		$cache_tags = array('get_products');
		if (is_null($storeView)) {
			$storeView = Cts_Registry::get('magento_default_store_id', 'commerce');
		} else {
			$storeView = $this->formatStoreId($storeView);
		}
		$cache_name .= '_'.$storeView;
		if (is_null($attributes)) {
			$attributes = array();
		}
		$cache_name .= '_'.md5(serialize($attributes));
		if (is_null($sort)) {
			$sort = array();
		}
		$cache_name .= '_'.md5(serialize($sort));
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product.list',
				array(
					'filters' => $filters,
					'store' => $storeView,
					'attributes' => $attributes,
					'sort' => $sort,
				)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $this->_filter_pagination($output, 'products');
	}

	/*
		Function: searchProducts
	*/
	function searchProducts($query, $storeView = null, $attributes = null) {
		$cache_name = __function__;
		$cache_name .= '_'.md5(serialize($query));
		$cache_tags = array('search_products');
		if (is_null($attributes)) {
			$attributes = array();
		}
		$cache_name .= '_'.md5(serialize($attributes));
		if (is_null($storeView)) {
			$storeView = Cts_Registry::get('magento_default_store_id', 'commerce');
		} else {
			$storeView = $this->formatStoreId($storeView);
		}
		$cache_name .= '_'.$storeView;
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product.search',
				array(
					'query' => $query,
					'store' => $storeView,
					'attributes' => $attributes,
				)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getSpecials
	*/
	function getSpecials($storeView = null, $activity_id = null) {
		$cache_name = __function__;
		$cache_tags = array('get_specials');
		if (is_null($storeView)) {
			$storeView = Cts_Registry::get('magento_default_store_id', 'commerce');
		} else {
			$storeView = $this->formatStoreId($storeView);
		}
		$cache_name .= '_'.$storeView;
		if (!is_null($activity_id)) {
			$cache_name .= '_'.$activity_id;
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_category.getSpecials',
				array(
					'store' => $storeView,
					'cat_id' => $activity_id,
				)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getProduct

		Arguments:
			$product_id - The ID of the product in the Magento database.
			$storeView - An integer of the Store ID, or a string of the Store Code. Default is false.
			$attributes - An array of attribute names to limit the results to. (Some required attributes will always be included.)
	*/
	function getProduct($product_id, $storeView = false, $attributes = false) {
		$cache_name = __function__;
		$cache_name .= '_'.$product_id;
		$cache_tags = array('product', (string)$product_id);
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= "_".$storeView;
		}
		$args = array();
		$args['product_id'] = $product_id;
		$args['store'] = ($storeView) ? $this->formatStoreId($storeView) : 0;
		if ($attributes && is_array($attributes)) {
			$args['attributes'] = $attributes;
			$cache_name .= "_".md5(serialize($attributes));
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product.info',
				$args
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getProductByUrlKey
	*/
	function getProductIdByUrlKey($url_key, $storeView = false) {
		$cache_name = __function__;
		$cache_name .= '_'.str_replace('-', '_', $url_key);
		$cache_tags = array('url_key', str_replace('-', '_', $url_key), 'product_url_key');
		$args = array();
		$args['url_key'] = $url_key;
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= "_".$storeView;
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product.getIdByUrlKey',
				$args
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getProductTypes
	*/
	function getProductTypes() {
		$cache_name = __function__;
		$cache_tags = array('product_types');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_type.list'
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: createProduct

		Arguments:
			$product_id - The ID or the SKU of the category in the Magento database.
	*/
	function createProduct($type = 'simple', $set, $sku, $productData) {
		$this->initSoap();
		$create = $this->client->call(
			$this->_api_session_id,
			'catalog_product.create',
			array(
				'type' => $type,
				'set' => $set,
				'sku' => $sku,
				'productData' => $productData
			)
		);
		
		if( $create ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by createProduct()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: get_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: search_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('search_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: product_url_key", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product_url_key'));
		}
		
		return $create;
	}

	/*
		Function: updateProduct

		Arguments:
			$product_id - The ID or the SKU of the product in the Magento database.
			$args - An array of database attribute=>value pairs
			$storeView - An integer of the Store ID, or a string of the Store Code. Default is false.
	*/
	function updateProduct($product_id, $args, $storeView = false) {
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
		} else {
			$storeView = NULL;
		}
		$this->initSoap();
		$update = $this->client->call(
			$this->_api_session_id,
			'catalog_product.update',
			array($product_id, $args, $storeView)
		);
		
		if( $update ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by updateProduct()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: get_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: product, {$product_id}", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product', (string)$product_id));
			if($this->logging) Cts_Log::report("Cache Clean :: search_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('search_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: get_specials", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_specials'));
														  
			if( is_array($args) && array_key_exists('categories', $args) && is_array($args['categories']) ){
				foreach( $args['categories'] as $category ){
					if($this->logging) Cts_Log::report("Cache Clean :: category_filters, {$category}", NULL, Zend_Log::INFO);
					$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_filters', (string)$category));
				}
			}
		}
		
		return $update;
	}

	/*
		Function: removeProduct

		Arguments:
			$product_id - The ID or the SKU of the category in the Magento database.
	*/
	function removeProduct($product_id, $parent_id=NULL) {
		$this->initSoap();
		$remove = $this->client->call(
			$this->_api_session_id,
			'catalog_product.delete',
			array($product_id)
		);
		
		if( $remove ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by removeProduct()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: product", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product', $product_id));
			if($this->logging) Cts_Log::report("Cache Clean :: get_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: search_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('search_products'));
			if($this->logging) Cts_Log::report("Cache Clean :: get_specials", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('get_specials'));
			if($this->logging) Cts_Log::report("Cache Clean :: category_filters", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('category_filters'));
			
			if( !is_null($parent_id) && $parent_id !== false ){
				if($this->logging) Cts_Log::report("Cache Clean :: product_children, {$parent_id}", NULL, Zend_Log::INFO);
				$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product_children', (string)$parent_id));
			} elseif( is_null($parent_id) && $parent_id !== false ){
				if($this->logging) Cts_Log::report("Cache Clean :: product_children :: Please pass removeProduct FALSE for better cache management.", NULL, Zend_Log::INFO);
				$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product_children'));
			}
		}
		
		return $remove;
	}

	/*
		Function: getProductAttributeSets
	*/
	function getProductAttributeSets() {
		$cache_name = __function__;
		$cache_tags = array('product_attribute_sets');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_attribute_set.list'
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getProductAttributeStore

		Arguments:
			$storeView - The ID of the attribute set.
	*/
	function getProductAttributeStore($storeView = null) {
		$cache_name = __function__;
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= '_'.$storeView;
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_attribute.currentStore',
				$storeView
			);
			$this->cache->save($output, $cache_name);
		}
		return $output;
	}

	/*
		Function: getProductAttributes

		Arguments:
			$setId - The ID of the attribute set.
	*/
	function getProductAttributes( $setId ) {
		$cache_name = __function__;
		$cache_name .= '_'.$setId;
		$cache_tags = array('product_attribute_set');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_attribute.list',
				$setId
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getProductAttributeGroups

		Arguments:
			$setId - The ID of the attribute set.
	*/
	function getProductAttributeGroups( $setId ) {
		$cache_name = __function__;
		$cache_name .= '_'.$setId;
		$cache_tags = array('product_attribute_groups');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_attribute.groups',
				$setId
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getProductAttributes

		Arguments:
			$setId - The ID of the attribute set.
	*/
	function getProductAttributeOptions($attributeId, $storeView = false) {
		$cache_name = __function__;
		$cache_name .= '_'.$attributeId;
		$cache_tags = array('product_attribute_options');
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= '_'.$storeView;
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_attribute.options',
				array(
					'attributeId' => $attributeId,
					'storeView'   => $storeView,
				)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getAttributeByCode
	*/
	function getAttributeByCode($code) {
		$cache_name = __function__;
		$cache_name .= '_'.$code;
		$cache_tags = array('product_attribute_by_code');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_attribute.loadByCode',
				array(
					'code' => $code,
				)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getLinkedProducts
	*/
	function getLinkedProducts($product_id, $type = "related") {
		$cache_name = __function__;
		$cache_name .= '_'.$product_id.'_'.$type;
		$cache_tags = array('linked_products', (string)$product_id);
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			return $this->client->call(
				$this->_api_session_id,
				'catalog_product_link.list',
				array($type, $product_id)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getRelatedProducts
	*/
	function getRelatedProducts($product_id, $storeView = false) {
		$cache_name = __function__;
		$cache_name .= '_'.$product_id;
		$cache_tags = array('related_products', (string)$product_id);
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= "_".$storeView;
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$tmp_related_products = $this->getLinkedProducts($product_id);
			$output = array();
			foreach ($tmp_related_products as $prod) {
				$output[] = $this->getProduct($prod['product_id'], $storeView);
			}
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: setLinkedProduct
	*/
	function setLinkedProduct($type='related', $product, $linkedProduct, $data=null) {
		$this->initSoap();
		$set = $this->client->call(
			$this->_api_session_id,
			'catalog_product_link.assign',
			array(
				'type' => $type,
				'product' => $product,
				'linkedProduct' => $linkedProduct,
				'data' => $data
			)
		);
		
		if( $set ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by setLinkedProduct()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: related_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('related_products', (string)$product));
			if($this->logging) Cts_Log::report("Cache Clean :: linked_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('linked_products', (string)$product));
		}
		
		return $set;
	}
	
	/*
		Function: updateLinkedProduct
	*/
	function updateLinkedProduct($type='related', $product, $linkedProduct, $data=null) {
		$this->initSoap();
		$update = $this->client->call(
			$this->_api_session_id,
			'catalog_product_link.update',
			array(
				'type' => $type,
				'product' => $product,
				'linkedProduct' => $linkedProduct,
				'data' => $data
			)
		);
		
		if( $update ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by updateLinkedProduct()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: related_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('related_products', (string)$product));
			if($this->logging) Cts_Log::report("Cache Clean :: linked_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('linked_products', (string)$product));
		}
		
		return $update;
	}
	
	/*
		Function: removeLinkedProduct
	*/
	function removeLinkedProduct($type='related', $product, $linkedProduct) {
		$this->initSoap();
		$remove = $this->client->call(
			$this->_api_session_id,
			'catalog_product_link.remove',
			array(
				'type' => $type,
				'product' => $product,
				'linkedProduct' => $linkedProduct,
			)
		);
		
		if( $remove ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by removeLinkedProduct()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: related_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('related_products', (string)$product));
			if($this->logging) Cts_Log::report("Cache Clean :: linked_products", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('linked_products', (string)$product));
		}
		
		return $remove;
	}

	/*
		Function: getProductLinkTypes
	*/
	function getProductLinkTypes() {
		$cache_name = __function__;
		$cache_tags = array('product_link_types');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product_link.types'
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getChildrenIds
	*/
	function getChildrenIds($product_id, $required = true) {
		$cache_name = __function__;
		$cache_name .= '_'.$product_id.'_'.(string)$required;
		$cache_tags = array('product_children', (string)$product_id);
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			// WARNING: this only works for configurable products!
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'product_type_configurable.getChildrenIds',
				array($product_id, $required)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}		
		return $output;
	}

	/*
		Function: getChildrenProducts
	*/
	function getChildrenProducts($product_id, $storeView, $attributes = null) {
		$cache_name = __function__;
		$cache_name .= '_'.$product_id;
		$cache_tags = array('product_children', (string)$product_id);
		if ($storeView) {
			$storeView = $this->formatStoreId($storeView);
			$cache_name .= '_'.$storeView;
		}
		if ($attributes) {
			$cache_name .= '_'.md5(serialize($attributes));
		}
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$children = array();
			$tmp_children = $this->getChildrenIds($product_id);
			// Magento workaround
			$tmp_children = (is_array($tmp_children[0])) ? $tmp_children[0] : $tmp_children;
			if (is_array($tmp_children)) {
				if ($storeView) {
					$storeView = $this->formatStoreId($storeView);
				}
				
				foreach ($tmp_children as $child) {
					$children[] = $this->getProduct($child, $storeView, $attributes);
				}
				
				usort($children, array($this, 'sortBySku'));
				$output = $children;
			} else {
				$output = false;
			}
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}
	
	
	protected function sortBySku( $a, $b ){
		if( $a == $b ){
			return 0;
		} elseif( $a['sku'] < $b['sku'] ){
			return -1;
		} else {
			return 1;
		}
	}
	

	/*
		Function: getChildrenIds
	*/
	function getParentIds($childId) {
		$cache_name = __function__;
		$cache_name .= '_'.$childId;
		$cache_tags = array('product_parent_ids');
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			// WARNING: this only works for configurable products!
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'product_type_configurable.getParentIds',
				array($childId)
			);
			$this->cache->save($output, $cache_name, $cache_tags);
		}
		return $output;
	}

	/*
		Function: getParentProducts
	*/
	function getParentProducts($childId) {
		return $this->getParentIds($childId);
	}

	/*
		Function: getChildrenIds
	*/
	function saveChildIds($product_id, $child_ids) {
		## WARNING: this only works for configurable products!
		$this->initSoap();
		$save = $this->client->call(
			$this->_api_session_id,
			'product_type_configurable.saveChildIds',
			array($product_id, $child_ids)
		);
		
		if( $save ){
			if($this->logging) Cts_Log::report("Cache Clean :: Run by saveChildIds()", NULL, Zend_Log::INFO);
			if($this->logging) Cts_Log::report("Cache Clean :: product_children", NULL, Zend_Log::INFO);
			$this->cache->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array('product_children', $product_id));
		}
		
		return $save;
	}

	/*
		Function: getWebsites
	*/
	function getWebsites() {
		$cache_name = __function__;
		$output = $this->cache->load($cache_name);
		if ($output === false || !isset($output)) {
			$this->initSoap();
			$output = $this->client->call(
				$this->_api_session_id,
				'catalog_product.getWebsites'
			);
			$this->cache->save($output, $cache_name);
		}
		return $output;
	}

}
